/*
 * Copyright (c) 2022, www.lgmgim.cn
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *	* Redistributions of source code must retain the above copyright
 * 	  notice, this list of conditions and the following disclaimer.
 * 	* Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 * 	  documentation and/or other materials provided with the distribution.
 * 	* Neither the name of the University of California, Berkeley nor the
 * 	  names of its contributors may be used to endorse or promote products
 * 	  derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file recoder_pack.h
 * @brief 
 * @author haibo.yang
 * @version 1.0
 * @date 2024-02-28
 * 
 * @copyright Copyright (c) 2023  lgim
 * 
 */
#include "zipper.h"
#include "std_srvs/srv/set_bool.hpp"
#include <boost/thread/thread.hpp> 
#include <future>
#include <queue>
#include <filesystem>
#include <agv_msgs/msg/agv_event_status.hpp>

#define CPU_ID    0

#define COMPRESS  0
#define CLEAN     1 
#define COLLECT   2 


namespace record {

namespace fs = std::filesystem; 
typedef std::pair<std::string, std::time_t> PackAge;
typedef std::pair<std::string, std::string> LOGFile; // filename, filepath

struct BlackBox {
	enum State {
		idle = 0,
		packing = 1,
	};

	int package_event;
    enum State state;
	rclcpp::Time package_time;
	std::queue<PackAge> package_queue;

	BlackBox():package_event(-1),
			   state(idle),
			   package_time(rclcpp::Time(0))
	{}

	void setEvent(int event, rclcpp::Node& node) 
	{
		package_event = event;
		package_time = node.now();
	}
};

class RecordPack: public rclcpp::Node
{
public:
	explicit RecordPack(const double interval);
	~RecordPack(); 
	
	/// @brief init for RecordPack;
	void Init();
    
	/// @brief shut down RecordPack thread;
	void ShutDown() { shutdown_req_ = true; }
    
	/// @brief get files;
	std::vector<LOGFile> ListFiles(const bool autotype, const fs::path& dir, const std::string& prefix = "");
    
	/// @brief symbol link dir;
	// bool IsSymbolicLink(const fs::path& path, fs::path& targetPath);

	/// @brief 
	void CleanUpExpired(const bool autotype);   // 2024.9.12 
    
	/// @brief delete dir;
	void DeleteAllExceptSymbolicLinkDir(const fs::path& path);

	/// @brief search log dir;
	int SearchDir(const bool autotype, const std::string path, const int code = COMPRESS);

	/// @brief check log dir 
	void CheckLogPath(void); 

	/// @brief ros package service callback;
	bool PackLogCallback(const std::shared_ptr<std_srvs::srv::SetBool::Request> req, 
			std::shared_ptr<std_srvs::srv::SetBool::Response> resp);
    
	/// @brief package log;
	void Package(const std::string name, const rclcpp::Time current, const int eventcode = 0);
    
	/// @brief Compression is completed after processing
	int DeleteAndCleanLogFiles(const std::string fileName, const std::string filePath, const bool autotype);

	/// @brief init parameters;
    bool init_parameters();

	/// @brief check black box, If the number exceeds, clear it
	void CheckPackage() 
	{
		while (blackbox_.package_queue.size() > max_package_count_) {
			if (blackbox_.state == BlackBox::State::idle) {
				if (Remove(blackbox_.package_queue.front().first) == 0)
					blackbox_.package_queue.pop();
			}
        }
	}

	/// @brief remove file;
	int Remove(const std::string path) 
	{
		fs::path filePath(path); 
		if (fs::exists(filePath)) {  
       		INFO("Deleted file: " << path);
			if (remove(path.c_str()) != 0) {  
				INFO("Try deleted file failed: " << path);  
				return 1;  
			} 
		}
		
		return 0;
	}
        
	/// @brief clear file contents;
	int Clear(const std::string path) 
	{
		// Clear file contents
		std::ofstream file(path, std::ios::out | std::ios::trunc);  

		if (!file.is_open()) {  
			ERROR("File open error: " << path);  
			return 1;  
		}  
	
		INFO("Clear file contents: " << path);
		file.close();  
		return 0;
	}
    
    /// @brief copy folder;
	void CopyFolder(const fs::path& source, const fs::path& destination) 
	{
	    try {
	        fs::copy(source, destination, fs::copy_options::recursive);
	        INFO("Folder copied successfully and original folder deleted.");
	    } catch(const fs::filesystem_error& e) {
	        INFO("Error copying folder: " << e.what());
	    }
	}
   
    /// @brief delete folder;
	void DeleteDirectory(const fs::path& path) {  
	    if (fs::exists(path)) {  
	        if (fs::is_directory(path)) {  
	            for (const auto& entry : fs::directory_iterator(path)) {  
	                DeleteDirectory(entry.path());  
	            }  
	            fs::remove_all(path);  
	        } else {  
	            fs::remove(path);  
	        }  
	    } else {  
	        INFO("Path does not exist: " << path);  
	    }  
	}  

	/// @brief record main thread;
	void Run();
    
	/// @brief Subscribe to events callbacks;
	void EventCallBack(const agv_msgs::msg::AGVEventStatus::SharedPtr event);
    
private:
    // std::string 			  path_;
	double                    interval_;
    bool 					  shutdown_req_;
    bool                      mask_auto_trigger_;
    bool                      estop_manual_trigger_;  //2025.02.08
    rclcpp::Service<std_srvs::srv::SetBool>::SharedPtr pack_server_;
    rclcpp::Subscription<agv_msgs::msg::AGVEventStatus>::SharedPtr event_sub_;
    agv_msgs::msg::AGVEventStatus  events_;

    Zipper*                   zipper_;
    std::mutex                mutex_;   
    int 					  max_package_count_; 
    int                       auto_trigger_files_; // 2024.9.12 auto trigger files

	std::future<void> 		  future_;
    struct BlackBox           blackbox_;      
	
  	boost::thread             thread_;
  	boost::thread             candump_thread_;

	rclcpp::Time 				  start_time_;
	float                     start_delay_;       
};
    
}